package fr.cephb.joperon.core.types;

import java.io.Serializable;
import java.util.Arrays;

import fr.cephb.util.JSONBuilder;
import fr.cephb.util.JSONizable;







/**
 * A wrapper over Mario Foglio's way to store the information Organism/Chromosome/position
 * The orginal information is stored in a char*
 * 
 * Human chromosome 05 position 64420
 * FID:   101051000064420
 * COUNT: 123456789012345
 * @author lindenb
 *
 */

public class Fid
implements Comparable<Fid>, Serializable,JSONizable
	{
	private static final long serialVersionUID = 1L;
	/** max length of the char* fid , ignoring last '\0' */
	private final static int FID_MAX_LENGTH=15;
	/** maximum position that can be stored in the fid */
	private final static int MAX_VALUE=999999999;
	private byte fid[];
	
	protected Fid()
		{
		/** empty constructor required for serialization */
		}
	
	/** maximum position that can be stored in the fid */
	public static int getMaxValue()
		{
		return MAX_VALUE;
		}
	
	/** max length of the char* fid , ignoring '\0' at end */
	public static int getLength()
		{
		return FID_MAX_LENGTH;
		}
	
	
	/** decode the fid from a String */
	public Fid(String s)
		{
		this(s.getBytes());
		}
	
	/** decode the fid from an array of bytes */
	public Fid(byte fid[])
		{
		if(fid.length!=getLength())
			{
			throw new IllegalArgumentException(
					"Bad fid "+new String(fid)+" length:"+fid.length+" != "+
					getLength());
			}
		
		this.fid=new byte[getLength()];
		System.arraycopy(fid, 0, this.fid, 0, this.fid.length);
		}
	
	/** create a fid from an Organsime/Chromosome/Position */
	public Fid(int org_id,int chrom_id,int position)
		{
		this();
		this.fid= new byte[getLength()];
		this.fid[0]='1';
		if(position<0) throw new IllegalArgumentException("position cannot be lower than 0");
		if(position> getMaxValue()) throw new IllegalArgumentException("position cannot be greater than "+getMaxValue()+" < "+position);
		if(org_id>9)
			{
			if(org_id>99) throw new IllegalArgumentException("bad org_id "+org_id);
			int unit= org_id - org_id/10;
			this.fid[1]=(byte) ((byte)'0'+(byte)(org_id-unit));
			this.fid[2]=(byte) ((byte)'0'+(byte)(unit));
			}
		else
			{
			if(org_id<1) throw new IllegalArgumentException("bad org_id "+org_id);
			this.fid[1]='0';
			this.fid[2]=(byte) ((byte)'0'+(byte)org_id);
			}
		
		if(chrom_id>9)
			{
			if(chrom_id>99) throw new IllegalArgumentException("bad chrom_id "+chrom_id);
			int unit= chrom_id - chrom_id/10;
			this.fid[3]=(byte) ((byte)'0'+(byte)(chrom_id-unit));
			this.fid[4]=(byte) ((byte)'0'+(byte)(unit));
			}
		else
			{
			if(chrom_id<1) throw new IllegalArgumentException("bad chrom_id "+chrom_id);
			this.fid[3]='0';
			this.fid[4]=(byte) ((byte)'0'+(byte)chrom_id);
			}
		this.fid[5]='1';
		String s= String.valueOf(position);
		while(s.length()< getLength()-6)
			{
			s="0"+s;
			}
		for(int i=0;i< s.length();++i)
			{
			this.fid[6+i]=(byte)s.charAt(i);
			}
		}
	
	/** return wether a fid in a char* is valid */
	public static final boolean isValid(byte fid[])
		{
		int i=0;
		while(i< fid.length && Character.isDigit(fid[i]) )
			{
			i++;
			}
		return i==getLength();
		}
	
	/** return if this fid is valid */
	public boolean isValid()
		{
		return Fid.isValid(this.fid);
		}
	
	/** extract the organism of this Fid */
	public int getOrganismId()
		{
		return (fid[1]-'0')*10+(fid[2]-'0');
		}
	
	/** extract the Chromosome of this Fid */
	public int getChromosomeId()
		{
		return (fid[3]-'0')*10+(fid[4]-'0');
		}
	
	/** extract the psoition of this Fid */
	public int getPosition()
		{
		int pos=0;
		for(int i=6;i< fid.length;++i)
			{
			pos= pos*10 + (fid[i]-'0');
			}
		return pos;
		}
	
	/** return an internal copy of the byte buffer */
	public byte[] getBytes()
		{
		byte[] copy=new byte[fid.length];
		System.arraycopy(this.fid, 0, copy, 0, this.fid.length);
		return copy;
		}
	
	/** return encode this fid as a char* */
	public String getString()
		{
		return new String(this.fid);
		}
	
	
	
	@Override
	public int hashCode() {
		return Arrays.hashCode(this.fid);
		}
	
	@Override
	public boolean equals(Object obj)
		{
		if(this==obj) return true;
		if(obj==null || getClass()!=obj.getClass()) return false;
		return Arrays.equals(this.fid, Fid.class.cast(obj).fid);
		}

	@Override
	public int compareTo(Fid o)
		{
		for(int i=0;i< fid.length;++i)
			{
			int n= this.fid[i]-o.fid[i];
			if(n!=0) return n;
			}
		return 0;
		}
	
	@Override
	public String toJSON()
		{
		return new JSONBuilder()
			.put("organism-id", getOrganismId())
			.put("chrom-id", getChromosomeId())
			.put("position", getPosition())
			.put("fid",getString())
			.toString();
		}
	
	@Override
	public String toString() {
		return toJSON();
		}
	
}
